package com.gitee.l0km.codegen.webclient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.l0km.codegen.base.CodeGenUtils;
import com.gitee.l0km.codegen.base.Method;
import com.gitee.l0km.com4j.base.Assert;
import com.gitee.l0km.com4j.base.exception.ServiceRuntimeException;

public class StubClient<T> extends WebClient<T> {
	private static final Logger logger = LoggerFactory.getLogger(StubClient.class);

	private final String stubPackage;
	private final String serviceName;
	private final String stubClassSimpleName;
	private final String stubClassName;

	public StubClient(Class<T> interfaceClass, Class<? extends T> refClass, Class<?> serviceClass, String stubPackage) {
		super(interfaceClass, refClass, serviceClass);
		Assert.notEmpty(stubPackage, "stubPackage");
		this.stubPackage = stubPackage;
		getImportedList().put("AxisFault", org.apache.axis2.AxisFault.class);
		getImportedList().put("RemoteException", java.rmi.RemoteException.class);
		serviceName = serviceClass.getSimpleName();
		stubClassSimpleName = serviceName + "Stub";
		stubClassName = stubPackage + "." + stubClassSimpleName;
	}

	public final Class<?> toStubClass(Method method) throws ClassNotFoundException {
		return toStubClass(method.getName());
	}

	public final Class<?> toStubClass(String methodName) throws ClassNotFoundException {
		return CodeGenUtils.forName(toStubClassName(methodName));
	}

	public final String toStubClassName(Method method) throws ClassNotFoundException {
		return toStubClassName(method.getName());
	}

	public final String toStubClassName(String methodName) {
		Assert.notEmpty(methodName, "methodName");
		StringBuffer buffer = new StringBuffer();
		buffer.append(stubPackage).append(".").append(stubClassSimpleName).append(".");
		buffer.append(CodeGenUtils.firstUpperCase(methodName));
		return buffer.toString();
	}

	public final String toStubClassName(Class<?> parameterClass) {
		Assert.notNull(parameterClass, "parameterClass");
		if (parameterClass.isArray())
			return toStubClassName(parameterClass.getComponentType()) + "[]";
		else {
			StringBuffer buffer = new StringBuffer();
			if(!CodeGenUtils.isBaseDataType(parameterClass))
				buffer.append(stubPackage).append(".").append(stubClassSimpleName).append(".");
			buffer.append(parameterClass.getSimpleName());
			return buffer.toString();
		}
	}

	public final Class<?> getParmameterClassForSetMethod(Class<?> methodClazz, String setMethod)
			throws NoSuchMethodException {
		Assert.notNull(methodClazz, "methodClazz");
		for (java.lang.reflect.Method set : methodClazz.getDeclaredMethods()) {
			if (set.getName().equals(setMethod))
				return set.getParameterTypes()[0];
		}
		throw new NoSuchMethodException();
	}

	public final String toSetMethod(String parameterName) {
		Assert.notEmpty(parameterName, "parameterName");
		StringBuffer buffer = new StringBuffer("set");
		buffer.append(CodeGenUtils.firstUpperCase(parameterName));
		return buffer.toString();
	}

	public final Class<?> toStubExpection(Class<? extends Throwable> expection) throws ClassNotFoundException {
		return CodeGenUtils.forName(toStubExpectionClassName(expection));
	}

	public final String toStubExpectionClassName(Class<? extends Throwable> expection) throws ClassNotFoundException {
		Assert.notNull(expection, "expection");
		StringBuffer buffer = new StringBuffer();
		buffer.append(stubPackage).append(".");
		buffer.append(serviceName).append(expection.getSimpleName()).append("Exception");
		return buffer.toString();
	}

	public final Class<?> toExpection(Method method, Class<? extends Throwable> exception)
			throws ClassNotFoundException {
		Assert.notNull(exception, "exception");
		String name = exception.getSimpleName().replaceAll(serviceName + "(\\w+)Exception", "$1");
		if (ServiceRuntimeException.class.getSimpleName().equals(name))
			return ServiceRuntimeException.class;
		for (Class<?> exp : CodeGenUtils.toSet(method.getExceptionTypes())) {
			if (exp.getSimpleName().equals(name))
				return exp;
		}
		throw new ClassNotFoundException(String.format("Cant found Exception Class [%s] map to %s", name,
				exception.getSimpleName()));
	}

	@Override
	public boolean compile() {
		boolean compileOk = false;
		try {
			if (super.compile()) {
				method2PortMap = createMethod2PortMapMap();
				addImportedClassFromMethods(method2PortMap.values(), true, true, true);
				compileOk = true;
			}
		} catch (NoSuchMethodException e) {
			logger.error(e.toString());
		}
		return compileOk;
	}

	/**
	 * @return stubClassSimpleName
	 */
	public String getStubClassSimpleName() {
		return stubClassSimpleName;
	}

	/**
	 * @return stubClassName
	 */
	public String getStubClassName() {
		return stubClassName;
	}

}
